{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#  python函数\n",
    "## 1 函数的引入\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "please input your age\n",
      "12\n",
      "you are so young\n"
     ]
    }
   ],
   "source": [
    "# c3_example.py\n",
    "str_age = input('please input your age\\n')\n",
    "if str_age.isdigit()== True:\n",
    "    age = int(str_age)\n",
    "    if age > 20:\n",
    "        print('you are too old')\n",
    "    else:\n",
    "        print('you are so young')\n",
    "\n",
    "else:\n",
    "    print('age must be digit')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "分析可以看出，上面代码完成的主要功能是：由用户从键盘输入一个有效的年龄（非数字的其它字符就不行），然后根据其年龄，档期小雨20时，就在屏幕上显示“you are too young”,否则，显示“you are too old”。可以把获取用户合法输入的过程，做成一个函数；还可以把后面的判断过程也写成一个函数，这样的好处是：\n",
    "1. 把大问题变成较小的问题，分别解决，符合人类解决问题的习惯。在本例子中，分解为了三个问题：即获得输入，判断输出，以及组合功能。\n",
    "2. 可以几种注意力分别编写每个函数，还可以获得别人已经写好的代码，实现代码复用，减少工作量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "please input your age\n",
      "2dcdc\n",
      "age must be digit\n",
      "please input your age\n",
      "dsdd\n",
      "age must be digit\n",
      "please input your age\n",
      "13\n",
      "you are so young\n"
     ]
    }
   ],
   "source": [
    "# c3_example.py\n",
    "def get_age():\n",
    "    while True:    \n",
    "        str_age = input('please input your age\\n')\n",
    "        if str_age.isdigit()== True:\n",
    "            age = int(str_age)\n",
    "            return age\n",
    "        else:\n",
    "            print('age must be digit')\n",
    "def panduan(age):\n",
    "    if age > 20:\n",
    "        print('you are too old')\n",
    "    else:\n",
    "        print('you are so young')\n",
    "        \n",
    "        \n",
    "age = get_age()\n",
    "panduan(age)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2 函数的定义和调用\n",
    "函数的概念，来自数学。表示变量到变量的一个固定映射。如函数$ y = 2 \\times x :x\\in R $，当$x = 2,4,6,8,\\cdots$时，必有$ y = 4,4,12,16,\\cdots$。\n",
    "\n",
    "在python程序中，定义一个函数，也是在生成一个对象。因此与语句`a = 10`生成一个int类的对象没有区别，只不过生成的是一个函数类的对象，且生成过程比较复杂。尤其是学过c或java的同学，其实内心深处不认为函数是对象，或者最起码认为函数很高级。\n",
    "\n",
    "定义函数的语法如下：\n",
    "```python\n",
    "\n",
    "def functionName(para1...):\n",
    "    functionBody\n",
    "    \n",
    "```\n",
    "\n",
    "- functionName：函数的名字，为以后使用该对象做准备。\n",
    "- para1: 函数的参数。不同的函数，参数数目不同，可以没有，也可以多个。\n",
    "- functionBody: 函数体。函数的代码块，是函数功能的实现语句。\n",
    "\n",
    "\n",
    "看下面的例子，定义一个计算两数之和的函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def sums(num1,num2):\n",
    "    num3 = num1 + num2\n",
    "    return num3"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "每个对象都有自己的功能，对象的功能通过其方法来体现。我们先看一下sums对象的方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__annotations__',\n",
       " '__call__',\n",
       " '__class__',\n",
       " '__closure__',\n",
       " '__code__',\n",
       " '__defaults__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__get__',\n",
       " '__getattribute__',\n",
       " '__globals__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__kwdefaults__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__name__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__qualname__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__']"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(sums)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意其中的第二行的`__call__`方法，表示该对象可以被呼唤（呼唤函数对象干什么，当然是让她展示自己的功能：完成映射，这在程序设计中称为调用）。由于这是函数对象被最常使用的方法，所以python为它提供了一个语法蜜糖。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "30\n",
      "30\n"
     ]
    }
   ],
   "source": [
    "n1 = 10\n",
    "n2 = 20\n",
    "n3 = sums.__call__(n1,n2)\n",
    "print(n3)\n",
    "\n",
    "n3 = sums(n1,n2)\n",
    "print(n3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "仔细体会上面的例子，主要专注\n",
    "- 从功能上看，函数是一段实现具体功能的代码。通过使用函数，可以实现代码的复用。\n",
    "- 从语法的角度看，它是一个复杂语句，可以包含多行，还可以包含多个快\n",
    "- 从执行过程看，函数只有在被调用时，才执行代码块的内容，执行过程与一般代码相同。函数被调用时，一般有0到多个参数对象，且必有返回对象。函数的多次调用之间没有关系。\n",
    "- 关于return。每个函数必有一个return，用于返回一个对象。它是函数执行的最后一句。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 参数匹配\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "positional argument follows keyword argument (<ipython-input-8-ac3c4c334a49>, line 26)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-8-ac3c4c334a49>\"\u001b[0;36m, line \u001b[0;32m26\u001b[0m\n\u001b[0;31m    fun_many_para(1,p4 = 3,2,3)\u001b[0m\n\u001b[0m                          ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n"
     ]
    }
   ],
   "source": [
    "#%%writefile aboutFunction/para1.py\n",
    "def my_pow(p1,p2):\n",
    "    return p1**p2\n",
    "print(my_pow(2,3))   #1 位置匹配\n",
    "print(my_pow(3,2))\n",
    "\n",
    "list = [2,3]\n",
    "print(my_pow(*list))  #使用列表 \n",
    "\n",
    "\n",
    "print(my_pow(p2 = 3, p1 = 2))  #2 key指定\n",
    "\n",
    "dict1 = {'p1':2,'p2':3}    #使用字典\n",
    "print(my_pow(**dict1))\n",
    "\n",
    "\n",
    "\n",
    "print(my_pow(2, p2 = 3))  # 3 位置与名字混合\n",
    "\n",
    "print('test')\n",
    "def fun_many_para(*first,p4=\"1\"):\n",
    "    for i in first:\n",
    "        print(i,end=' ')\n",
    "    print(p4)\n",
    "    return \"god bless you\"\n",
    "#fun_many_para(1,p4 = 3,2,3)\n",
    "print(1,)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 特殊参数的函数\n",
    "一种是带有缺省参数的函数；另一类是参数数目不确定的函数。\n",
    "注意：\n",
    "- 带\\*的参数，表示这些从当前位置开始带最后的一个位置参数，都将被被组装为一个list对象，在函数内进行处理。所以带\\*的参数只能是最后一个参数。\n",
    "- 带\\*\\*的参数，表示这些从当前位置开始带最后的一个key参数（关键字参数），都将被被组装为一个dict对象，在函数内进行处理。所以带\\*的参数只能是最后一个参数。\n",
    "- 是为了处理参数的数目变化不定的情形。并不是说这个参数是一个list或dict，其实python并不关心每个参数的类型，因为她认为每个参数都是对象。\n",
    "\n",
    "通过下面的程序可以仔细分辨。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting aboutFunction/para2.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile aboutFunction/para2.py\n",
    "def my_pow_def(p1,p2 =2):  #default para\n",
    "    return p1**p2\n",
    "print(my_pow_def(3))\n",
    "\n",
    "def my_sum(*nums):        #list para\n",
    "    sum = 0\n",
    "    for i in nums:\n",
    "        print(i,end = ' ')\n",
    "        sum = sum + i\n",
    "    print('\\n')\n",
    "    return sum\n",
    "print(my_sum(1,2))\n",
    "print(my_sum(1,2,3,4))\n",
    "'''\n",
    "#会报错，为什么？\n",
    "l1 = [1,2,3]\n",
    "print(my_sum(l1))\n",
    "'''\n",
    "def my_test(**pstu):\n",
    "    for k,v in pstu.items():\n",
    "        print(type(k),type(v))\n",
    "        print(k,v)\n",
    "my_test(p1 = 'zs', p2 = 'ls')\n",
    "\n",
    "'''\n",
    "#会报错，为什么？\n",
    "stu = {'p1':'zs','p2':'ls'}\n",
    "my_test(stu)\n",
    "\n",
    "'''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们熟悉的`print`函数就具备这两者特点，可以通过`help`函数看一下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function print in module builtins:\n",
      "\n",
      "print(...)\n",
      "    print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n",
      "    \n",
      "    Prints the values to a stream, or to sys.stdout by default.\n",
      "    Optional keyword arguments:\n",
      "    file:  a file-like object (stream); defaults to the current sys.stdout.\n",
      "    sep:   string inserted between values, default a space.\n",
      "    end:   string appended after the last value, default a newline.\n",
      "    flush: whether to forcibly flush the stream.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(print)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  名字分类\n",
    "\n",
    "\n",
    "这里首先要明白一个重要的概念，在python中，所有与name相关的操作只有两种情况：\n",
    "1. 定义名字，也就是给对象命名，即把一个名字绑定给一个对象。如常见的a=1,s ='123',还有上面的函数定义语句def，后面还会见到一些。\n",
    "2. 使用名字，即使用名字后面的那个对象。\n",
    "\n",
    "\n",
    "因此，面对name，我们先要明确是那种情况。如下面的语句：\n",
    "```pyton\n",
    "a = 10\n",
    "b = a +1\n",
    "```\n",
    "上面第1句就是给对象命名；第2句中则两种情况都有，名字a是在使用一个对象，而b则是为新对象命名。再看下面的代码：\n",
    "```python\n",
    "a = 10\n",
    "a = a + 1\n",
    "```\n",
    "第2句中，等号右边的a，是在使用一个对象，而左边的a，则是为新的对象命名。\n",
    ">在python语法中，等号的作用就是为对象命名。\n",
    "\n",
    "另外，还要时常提醒自己如下几点：\n",
    "1. 表面上我们在处理名字，实际上我们关注的是名字后面的那个对象。\n",
    "2. 每一个名字后面都有一个且只有一个对象。\n",
    "3. 名字是使用对象的唯一的线索，也是对象不被消除的唯一希望。（这和皮克斯电影《寻梦环游记》（Coco）中所表现的一样：没有人记得的亡灵就会永远消失）\n",
    "\n",
    "有了以上的准备，我们就可以研究名字了。python模块内的名字，根据其被定义的位置，只能属于一下两种之一：\n",
    "\n",
    "1. 定义在function函数块内的名字，称为本地名字（local name）。\n",
    "2. 定义在module模块中的名字，称为全局名字（global name）。\n",
    "\n",
    "\n",
    "特别注意，**函数的参数也是local name**。这个其实很合理，因为参数就是函数为作为参数的对象定义的名字。\n",
    "\n",
    "\n",
    "看下面的代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20\n",
      "100\n",
      "64\n",
      "100\n",
      "512\n"
     ]
    }
   ],
   "source": [
    "#%%writefile aboutFunction/nametype.py\n",
    "\n",
    "a = 10\n",
    "def fun1():\n",
    "    d = 10\n",
    "    b = a + d\n",
    "    return b\n",
    "c = fun1()\n",
    "print(c)\n",
    "\n",
    "def pow_factory(para):\n",
    "    \n",
    "    def sub_fun1(p):\n",
    "        return p ** 2\n",
    "    def sub_fun2(p):\n",
    "        return p ** 3\n",
    "    print(sub_fun1(a))\n",
    "    if para == 2:\n",
    "        return sub_fun1\n",
    "    else:\n",
    "        return sub_fun2\n",
    "pow2 = pow_factory(2)\n",
    "print(pow2(8))\n",
    "pow3 = pow_factory(3)\n",
    "print(pow3(8))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "分析如下:\n",
    "- a、c和fun1都是global name;\n",
    "- b和d是local name\n",
    "- 名字分为local和global，但对象只存在出生地不同（函数调用时在函数内及函数外），但不存在这种区分。\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 名字的作用域\n",
    "\n",
    "确定了名字的分类，就可以学习名字的可访问性。\n",
    "\n",
    "\n",
    "名字的可访问性是指在执行当前语句时，对某一个名字的可访问性。还是不明白吧。想一下，我们关心名字是假，其实是想用名字后面的的那个对象。所以名字的可访问性，就是指在执行当前代码时，能否用这个名字使用这个名字后面的那个对象。如果可以，那就称为该名字是可访问的，否则，称为不可访问。如上面的第7句，对名字c就是可访问的，因为你可以拿到这个名字后面的对象。\n",
    "\n",
    "\n",
    "python中名字的可访问性判定原则很简单，如下两句：\n",
    "1. global name对模块中的所有语句都是可访问的。\n",
    "2. lcoal name只对本函数内的语句是可访问的。\n",
    "由于python是个解释性的程序，所以对于还没有执行到的语句，就没有任何作用。所以上面的原则更严格第叙述为：\n",
    "1. global name对模块中的所有位于其定义后的语句都是可访问的。\n",
    "2. lcoal name只对本函数内的所有位于其定义的语句是可访问的。\n",
    "注意这里前后指的是执行顺序。\n",
    "\n",
    "在上看的例子中，名字a，在第4句是可访问的，因为它是global name，且这句在执行时位于a的定义之后。注意函数只有在被调用时才能执行，所以把a = 1的定义换一下，仍然是可访问的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20\n"
     ]
    }
   ],
   "source": [
    "def fun1():\n",
    "    d = 10\n",
    "    b = a + d\n",
    "    return b\n",
    "a = 10\n",
    "c = fun1()\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "名字b对第5句是可访问的；名字c对第7句是可访问的；名字d对第3句是可访问的。\n",
    "另外，经常把对一个名字可访问的所有语句称为该名字的作用域。从而有如下的规则：\n",
    "1. global name的作用域为全模块\n",
    "2. local name的作用域为其所在函数内部。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n"
     ]
    }
   ],
   "source": [
    "def fun1():\n",
    "    global d\n",
    "    d = 10\n",
    "    b = a + d\n",
    "    return b\n",
    "a = 10\n",
    "c = fun1()\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的第2行，使用global将一个local name变为了global name。这种很容易引起混乱，不建议使用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 名字冲突\n",
    "是指同一个名字，又是global name，又是local name。注意这种情况并不一定就是错误。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n"
     ]
    }
   ],
   "source": [
    "a = 10\n",
    "def fun1():\n",
    "    a = 20\n",
    "    b = a + d\n",
    "    return b\n",
    "c = fun1()\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "名字a就发生了冲突，但这不是错误。只要记住，函数内部local name优先。即在执行第4句时，属于local name的a的作用域，所以这个a=20，而与外面的a无关。而第7句的a，当然是global的那个a。\n",
    "\n",
    "\n",
    "另外，要记住函数的参数也是local name，如下面："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20\n",
      "10\n"
     ]
    }
   ],
   "source": [
    "a = 10\n",
    "def fun1(d):\n",
    "    b = a + d\n",
    "    d = 20\n",
    "    return b\n",
    "c = fun1(a)\n",
    "print(c)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4 函数的副作用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting aboutFunction/sideeffect.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile aboutFunction/sideeffect.py\n",
    "def sum_normal(plist):\n",
    "    sum = 0;\n",
    "    for i in plist:\n",
    "        sum = sum + i\n",
    "    t = sum /len(plist)\n",
    "    return t\n",
    "\n",
    "def sum_bad(plist):\n",
    "    sum = 0;\n",
    "    for i in plist:\n",
    "        sum = sum + i\n",
    "    t = sum /len(plist)\n",
    "    plist[0] = 10\n",
    "    return t\n",
    "\n",
    "def sum_good(plist):\n",
    "    plist = plist[:]\n",
    "    sum = 0;\n",
    "    for i in plist:\n",
    "        sum = sum + i\n",
    "    t = sum /len(plist)\n",
    "    plist[0] = 20\n",
    "    return t\n",
    "\n",
    "list1 = [1,2,3]\n",
    "\n",
    "#print(sum_normal(list1))\n",
    "\n",
    "#print(sum_bad(list1))\n",
    "#print(list1[0])\n",
    "\n",
    "\n",
    "#print(sum_good(list1))\n",
    "#print(list1[0])\n",
    "\n",
    "list2 = list1[:]  \n",
    "print(sum_bad(list2))\n",
    "print(list1[0])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 递归函数\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
